﻿using System;
using System.Collections;
using System.Linq;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;

namespace YArchitech.Plumbing
{
	public class FireHydrantByWall
	{
		public FireHydrantByWall(UIDocument UiDoc)
		{
			this.uiDoc = UiDoc;
		}

		public SelectedElement PickElementsByFace(UIDocument uiDocument, string prompt, ref Reference refer, ref Transform trans, double searchLength = 0.05)
		{
			SelectedElement result;
			try
			{
				Reference reference = uiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.PointOnElement, prompt);
				refer = reference;
				if (reference == null)
				{
					result = null;
				}
				else
				{
					Element element = uiDocument.Document.GetElement(reference);
					XYZ globalPoint = reference.GlobalPoint;
					RevitLinkInstance revitLinkInstance = element as RevitLinkInstance;
					if (revitLinkInstance != null)
					{
						trans = revitLinkInstance.GetTransform();
					}
					if (revitLinkInstance == null)
					{
						result = new SelectedElement
						{
							SelectedPoint = globalPoint,
							Element = ((element is Wall || element is ModelLine || this.isColumn(element)) ? element : uiDocument.Document.GetElements(globalPoint, searchLength).FirstOrNull<Element>())
						};
					}
					else
					{
						result = new LinkedDocument(revitLinkInstance).GetSelectedElementByWallOrColumn(globalPoint, searchLength);
					}
				}
			}
			catch (Autodesk.Revit.Exceptions.OperationCanceledException ex)
			{
				ex.ToString();
				result = null;
			}
			catch (Exception ex2)
			{
				throw ex2;
			}
			return result;
		}

		private bool isColumn(Element ele)
		{
			int integerValue = ((FamilyInstance)ele).Category.Id.IntegerValue;
			return ele is FamilyInstance && (integerValue == -2001330 || integerValue == -2000100);
		}

		private Curve getColumnCurve(XYZ selectedPoint, XYZ locationPoint)
		{
			XYZ xyz = selectedPoint - new XYZ(locationPoint.X, locationPoint.Y, selectedPoint.Z);
			if (xyz.X == 0.0)
			{
				return new YJKLine(selectedPoint, selectedPoint + new XYZ(1.0, 0.0, selectedPoint.Z)).Line;
			}
			if (xyz.Y == 0.0)
			{
				return new YJKLine(selectedPoint, selectedPoint + new XYZ(0.0, 1.0, selectedPoint.Z)).Line;
			}
			double num = 1.0;
			double num2 = -(xyz.X * num / xyz.Y);
			return new YJKLine(selectedPoint, selectedPoint + new XYZ(num, num2, selectedPoint.Z)).Line;
		}

		public Curve SelectWallAndColumn(Transform trans, ref XYZ selectedPoint)
		{
			Reference reference = null;
			Line result = null;
			try
			{
				SelectedElement selectedElement = this.PickElementsByFace(this.uiDoc, "请选择要布置的参考墙面或者柱面", ref reference, ref trans, 0.05);
				if (selectedElement == null)
				{
					return result;
				}
				Element element = selectedElement.Element;
				if (element is ModelLine)
				{
					selectedPoint = selectedElement.SelectedPoint;
					return ((ModelLine)element).GeometryCurve;
				}
				if (element is Wall || element is FamilyInstance)
				{
					result = this.getWall(element, reference, trans, ref selectedPoint, selectedElement);
				}
			}
			catch (Autodesk.Revit.Exceptions.OperationCanceledException ex)
			{
				throw ex;
			}
			catch (Exception)
			{
				return result;
			}
			return result;
		}

		private Line getWall(Element ele, Reference reference, Transform trans, ref XYZ selectedPoint, SelectedElement se)
		{
			new Options().ComputeReferences = true;
			Face face = null;
			try
			{
				GeometryObject geometryObjectFromReference = ele.GetGeometryObjectFromReference(reference);
				if (geometryObjectFromReference is CylindricalFace)
				{
					face = (geometryObjectFromReference as CylindricalFace);
				}
				else if (geometryObjectFromReference is PlanarFace)
				{
					face = ((geometryObjectFromReference as Face) as PlanarFace);
				}
			}
			catch (Exception)
			{
				face = this.GetNearestFaceFromElement(ele, reference.GlobalPoint, trans);
			}
			Line line = this.getLine(face, reference, trans, se.SelectedPoint, ele);
			if (line != null)
			{
				selectedPoint = line.Project(se.SelectedPoint).XYZPoint;
			}
			return line;
		}

		private void setPt(ref XYZ firstPt, ref XYZ secondPt, XYZ item, XYZ selectPt)
		{
			if (firstPt == null)
			{
				firstPt = new XYZ(item.X, item.Y, selectPt.Z);
				return;
			}
			if (secondPt == null)
			{
				double num = firstPt.DistanceTo(selectPt);
				if (item.DistanceTo(selectPt) < num)
				{
					secondPt = firstPt;
					firstPt = new XYZ(item.X, item.Y, selectPt.Z);
					return;
				}
				secondPt = new XYZ(item.X, item.Y, selectPt.Z);
				return;
			}
			else
			{
				double num2 = firstPt.DistanceTo(selectPt);
				double num3 = secondPt.DistanceTo(selectPt);
				double num4 = new XYZ(item.X, item.Y, selectPt.Z).DistanceTo(selectPt);
				if (num2 < num4 && num3 > num4)
				{
					secondPt = new XYZ(item.X, item.Y, selectPt.Z);
					return;
				}
				if (num2 > num4 && num3 > num4)
				{
					secondPt = firstPt;
					firstPt = new XYZ(item.X, item.Y, selectPt.Z);
				}
				return;
			}
		}

		private double GetB(XYZ point1, XYZ point2)
		{
			if (point1.X - point2.X == 0.0)
			{
				return double.MaxValue;
			}
			double num = (point1.Y - point2.Y) / (point1.X - point2.X);
			return point1.Y - num * point1.X;
		}

		private Line getLine(Face face, Reference reference, Transform trans, XYZ SelectedPoint, Element ele)
		{
			Transform insTrans = null;
			double num = double.MaxValue;
			if (ele is FamilyInstance)
			{
				foreach (GeometryObject geometryObject in ele.get_Geometry(new Options
				{
					ComputeReferences = true
				}))
				{
					if (geometryObject is GeometryInstance)
					{
						insTrans = ((GeometryInstance)geometryObject).Transform;
						break;
					}
				}
			}
			Line line = null;
			foreach (object obj in face.EdgeLoops.get_Item(0))
			{
				Edge edge = (Edge)obj;
				if (edge.Tessellate().Count == 2)
				{
					if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) < 0.01)
					{
						Line faceLine = this.getFaceLine(edge.Tessellate()[0], edge.Tessellate()[1], null);
						double num2 = faceLine.Distance(reference.GlobalPoint);
						if (num2 < num)
						{
							num = num2;
							line = faceLine;
						}
					}
				}
				else
				{
					XYZ xyz = null;
					XYZ xyz2 = null;
					foreach (XYZ item in edge.Tessellate())
					{
						this.setPt(ref xyz, ref xyz2, item, SelectedPoint);
					}
					if (xyz != null && xyz2 != null && new YJKLine(xyz, xyz2).IsPointOnLine(SelectedPoint))
					{
						line = Line.CreateBound(xyz, xyz2);
						double num3 = line.Distance(reference.GlobalPoint);
						if (trans != null)
						{
							num3 = line.Distance(reference.GlobalPoint - trans.Origin);
						}
						if (num3 < num)
						{
							num = num3;
							break;
						}
						break;
					}
				}
			}
			if (num > 1.0)
			{
				foreach (object obj2 in face.EdgeLoops.get_Item(0))
				{
					Edge edge2 = (Edge)obj2;
					if (edge2.Tessellate().Count == 2)
					{
						if (Math.Abs(edge2.Tessellate()[0].Z - edge2.Tessellate()[1].Z) < 0.01)
						{
							Line faceLine2 = this.getFaceLine(edge2.Tessellate()[0], edge2.Tessellate()[1], insTrans);
							double num4 = faceLine2.Distance(reference.GlobalPoint);
							if (trans != null)
							{
								num4 = faceLine2.Distance(trans.OfPoint(reference.GlobalPoint));
							}
							if (num4 < num)
							{
								num = num4;
								line = faceLine2;
							}
						}
					}
					else
					{
						XYZ xyz3 = null;
						XYZ xyz4 = null;
						foreach (XYZ item2 in edge2.Tessellate())
						{
							this.setPt(ref xyz3, ref xyz4, item2, SelectedPoint);
						}
						if (xyz3 != null && xyz4 != null && new YJKLine(xyz3, xyz4).IsPointOnLine(SelectedPoint))
						{
							line = Line.CreateBound(xyz3, xyz4);
							double num5 = line.Distance(reference.GlobalPoint);
							if (trans != null)
							{
								num5 = line.Distance(reference.GlobalPoint - trans.Origin);
							}
							if (num5 < num)
							{
								num = num5;
								break;
							}
							break;
						}
					}
				}
			}
			return line;
		}

		private Line getFaceLine(XYZ pt1, XYZ pt2, Transform insTrans)
		{
			Line result;
			if (insTrans != null)
			{
				result = YJKLineEx.YJKGetBound(insTrans.OfPoint(pt1), insTrans.OfPoint(pt2));
			}
			else
			{
				result = YJKLineEx.YJKGetBound(pt1, pt2);
			}
			return result;
		}

		private PlanarFace GetNearestFaceFromElement(Element e, XYZ globalPoint, Transform trans)
		{
			PlanarFace result = null;
			try
			{
				foreach (GeometryObject geometryObject in e.get_Geometry(new Options
				{
					ComputeReferences = true
				}))
				{
					if (geometryObject is Solid)
					{
						Solid solid = geometryObject as Solid;
						if (!(solid != null) || solid.Edges.Size <= 0)
						{
							continue;
						}
						double num = double.MaxValue;
						IEnumerator enumerator2 = solid.Faces.GetEnumerator();
						{
							while (enumerator2.MoveNext())
							{
								object obj = enumerator2.Current;
								Face face = (Face)obj;
								if (this.isVerticalFace(face))
								{
									int num2 = 0;
									double num3 = double.MaxValue;
									foreach (object obj2 in face.EdgeLoops.get_Item(0))
									{
										Edge edge = (Edge)obj2;
										num2++;
										if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) < 0.1)
										{
											double num4 = Line.CreateBound(edge.Tessellate()[0], edge.Tessellate()[1]).Distance(globalPoint - trans.Origin);
											if (num4 < num3)
											{
												num3 = num4;
											}
										}
									}
									if (num3 < num)
									{
										num = num3;
										result = (face as PlanarFace);
									}
								}
							}
							continue;
						}
					}
					if (geometryObject is GeometryInstance)
					{
						foreach (GeometryObject geometryObject2 in RevitVersionFuncs.GetGeometryObjectArray((geometryObject as GeometryInstance).GetInstanceGeometry()))
						{
							Solid solid2 = geometryObject2 as Solid;
							if (solid2 != null && solid2.Edges.Size > 0)
							{
								double num5 = double.MaxValue;
								foreach (object obj3 in solid2.Faces)
								{
									Face face2 = (Face)obj3;
									if (this.isVerticalFace(face2))
									{
										int num6 = 0;
										double num7 = double.MaxValue;
										foreach (object obj4 in face2.EdgeLoops.get_Item(0))
										{
											Edge edge2 = (Edge)obj4;
											num6++;
											if (Math.Abs(edge2.Tessellate()[0].Z - edge2.Tessellate()[1].Z) < 0.1)
											{
												double num8 = Line.CreateBound(edge2.Tessellate()[0], edge2.Tessellate()[1]).Distance(globalPoint - trans.Origin);
												if (num8 < num7)
												{
													num7 = num8;
												}
											}
										}
										if (num7 < num5)
										{
											num5 = num7;
											result = (face2 as PlanarFace);
										}
									}
								}
							}
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return result;
		}

		private bool isVerticalFace(Face f)
		{
			foreach (object obj in f.EdgeLoops.get_Item(0))
			{
				Edge edge = (Edge)obj;
				if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) > 0.1)
				{
					return true;
				}
			}
			return false;
		}

		private UIDocument uiDoc;
	}
}
